package client

import (
	"echo_client/config"
	"echo_client/pkg/model"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"sync"
	"time"

	"github.com/gorilla/websocket"
)

const (
	defaultTimeout = 10 * time.Second
)

// Client .
type Client struct {
	Mtx   *sync.Mutex
	Conn  *websocket.Conn
	Error chan error // 错误
}

// Close .
func (c *Client) Close() {
	err := c.Conn.Close()
	if err != nil {
		c.Error <- fmt.Errorf("连接关闭,err:%v\n", err)
		return
	}
}

// defaultDialer .
var defaultDialer = websocket.Dialer{
	Proxy:            http.ProxyFromEnvironment,
	HandshakeTimeout: defaultTimeout,
}

// NewClient .
func NewClient() *Client {
	hostname, _ := os.Hostname()
	config.HostName = hostname
	conn, _, err := defaultDialer.Dial(
		fmt.Sprintf("%s%s", config.C.PingApi, hostname), nil)
	if err != nil {
		panic(fmt.Errorf("连接远程服务器错误,err:%v\n", err))
	}
	return &Client{
		Mtx:   &sync.Mutex{},
		Conn:  conn,
		Error: make(chan error),
	}
}

// SendPollingMsg 发送轮询消息
func (c *Client) SendPollingMsg() {
	msg := model.NewPingMessage(config.HostName, "system", time.Now().Unix())
	ping, err := json.Marshal(msg)
	if err != nil {
		c.Error <- err
		return
	}
	go c.TickerSend(config.C.PingPolling, ping)
}

// ReceiveHandler 接收消息处理
func (c *Client) ReceiveHandler() {
	for {
		data, err := c.receive()
		if err != nil {
			c.Error <- err
			return
		}
		var msg model.MessageV1
		err = json.Unmarshal(data, &msg)
		if err != nil {
			c.Error <- err
			return
		}
		switch msg.Typ {
		case model.PongMessage:
			continue
		case model.CsMachineMessage:
			c.SendSystemInfo(&msg)
		}
	}
}

// setWriteDeadline .
func (c *Client) setWriteDeadline(polling time.Duration) {
	err := c.Conn.SetWriteDeadline(time.Now().Add(config.C.Timeout + polling))
	if err != nil {
		c.Error <- err
		return
	}
}

// setReadDeadline .
func (c *Client) setReadDeadline() {
	err := c.Conn.SetReadDeadline(time.Now().Add(config.C.Timeout))
	if err != nil {
		c.Error <- err
		return
	}
}

// receive 接收消息
func (c *Client) receive() ([]byte, error) {
	c.setReadDeadline()
	_, msg, err := c.Conn.ReadMessage()
	if err != nil {
		return nil, err
	}
	return msg, nil
}

// TickerSend 发送同一条消息
func (c *Client) TickerSend(polling time.Duration, msg []byte) {
	ticker := time.NewTicker(polling)
	for range ticker.C {
		c.setWriteDeadline(polling)
		c.Mtx.Lock()
		err := c.Conn.WriteMessage(websocket.TextMessage, msg)
		if err != nil {
			c.Error <- err
			c.Mtx.Unlock()
			return
		}
		c.Mtx.Unlock()
	}
}

// SendOneMsg 发送一条消息
func (c *Client) SendOneMsg(msg []byte) {
	c.Mtx.Lock()
	defer c.Mtx.Unlock()
	c.setWriteDeadline(0)
	err := c.Conn.WriteMessage(websocket.TextMessage, msg)
	if err != nil {
		c.Error <- err
		return
	}
}

// DealHandler 处理器
func (c *Client) DealHandler() {
	for {
		select {
		case err := <-c.Error: // 错误处理
			log.Printf("[ERROR]%s\n", err)
		}
	}
}
